#----------------------------------------------------------------------
#
#  Copyright 2007, Thomas Dejanovic.
# 
#  This is free software; you can redistribute it and/or modify it
#  under the terms of the GNU Lesser General Public License as
#  published by the Free Software Foundation; either version 2.1 of
#  the License, or (at your option) any later version.
# 
#  This software is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
#  Lesser General Public License for more details.
# 
#  You should have received a copy of the GNU Lesser General Public
#  License along with this software; if not, write to the Free
#  Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
#  02110-1301 USA, or see the FSF site: http://www.fsf.org.
#
#----------------------------------------------------------------------
#
#  This is the most specific level of verilog generation for register
#  hatchlings.  it contains information about how to write verilog
#  from the hatchling data structure.
#
#----------------------------------------------------------------------
id = "$Id: hatch_register_verilog.py 667 2010-07-01 00:12:17Z jayshurtz $"
# $URL: http://hatch.googlecode.com/svn/trunk/hatch/hatch_targets/verilog/obsolete/hatch_register_verilog.py $
# $Author: jayshurtz $
#----------------------------------------------------------------------

version = " ".join(id.split()[1:3])

import re
import math
from hatch_verilog import *

#----------------------------------------------------------------------
# Notes:
#

# wires declared with a width of None imply the width is unknown and
# are a place holder for a wire or register declaration somewhere else
# in the design.  if any wires of width None are found when writing
# out the design, it imples that a signal is used in some expression
# that is not preovided in the register definition file.

# wire and reg definitions can multiply define the same register as
# long as the width of all the definitions are the same.  the multiple
# definitions are collapsed into a single definition with reg taking
# precidence over wire.  i.e. if a function requires a reg and all the
# others require a wire, a reg will be defined.

# 'sw'='t' registers are special cases and cannot implement any other
# functions other than being readable in which case they always return
# '0'.

# XXXXXX - run_on_all_return_or() hatch_base method is intended to be
# used to find out if any part of the hatchling structure defines a
# property without requireing the property to be propogated to the top
# of the tree or defined at the top of the tree.  In particular this
# is intended to find error detection and correction flags as this
# infers ports on the module definition if they are defined (when this
# is implemented).

# We could also add an address decode pipeline stage if required.  add
# it as an option.  we could do this by generating a register name
# select term that may or may not be pipelined (depending on the
# config bit).  then AND it with the write enable signal.  for a bunch
# of 1 bit registers, 1 bit of pipeliend register select might be a
# little over the top.

# XXXXXX - verify that there is only one level of pack in the hieracy
# and that it only contains hatch leaves.

# XXXXXX - implement keyed registers or support the sw_load_enable
# property.

# XXXXXX - make the default trigger be a single bit unless the trigger
# mode be 'bus' or something.

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

#----------------------------------------------------------------------

uniqueNumber = 0

uniqueNameHash = {}

#======================================================================
#
# collection of register access untility functions.
#
#======================================================================

def get_verilog_port_list(hatchling):
    
    """Return a list of verilog ports implied by this hatchling node"""

    r = []

    if hatchling.property('type') == 'register':

        w = 0
        try:
            w = int(hatchling.property('width'))
        except ValueError:
            raise "*** ERROR: cannot figure out the width of register %s.\n"%(hatchling.name)

        width = ""
        if w > 1:
            width = "[%d:0]"%(w -1)

        if hatchling.has_property('external') :
            # hatchling node is external, so all register functions
            # are implemented outside via a standard 'bus' interface.
            r.append(["input ", width, "%s_in"%(hatchling.name)])
            r.append(["output", width, "%s_out"%(hatchling.name)])
            r.append(["output", "",    "%s_we"%(hatchling.name)])
        else:

            if hatchling.has_property('hw'):

                if  re.match("^.*r.*$", hatchling.property('hw')):
                    r.append(["output", width, hatchling.name])

                if  re.match("^.*w.*$", hatchling.property('hw')):
                    r.append(["input ", width, "%s_in"%(hatchling.name)])
                    r.append(["input ", "",    "%s_we"%(hatchling.name)])

                if  re.match("^.*d.*$", hatchling.property('hw')):
                    r.append(["input ", "",    "%s_dec"%(hatchling.name)])

                if re.match("^.*i.*$", hatchling.property('hw')):
                    r.append(["input ", "",    "%s_inc"%(hatchling.name)])

                if re.match("^.*c.*$", hatchling.property('hw')):
                    r.append(["input ", "",    "%s_clear"%(hatchling.name)])

                if re.match("^.*s.*$", hatchling.property('hw')):
                    r.append(["input ", "",    "%s_set"%(hatchling.name)])

                if re.match("^.*p.*$", hatchling.property('hw')):
                    r.append(["input ", "",    "%s_preset"%(hatchling.name)])

    if hatchling.has_property('input_ports'):
        # this hatchling node has some explicitly declared input ports.
        inputPorts = hatchling.property('input_ports')
        inputPorts = inputPorts.replace('\n','')
        while inputPorts.rfind('  ') > 0:
            inputPorts=inputPorts.replace('  ',' ')
        for p in inputPorts.split(','):
            # check for a port size.
            pl = p.split()
            if (len(pl) == 2):
                r.append(["input ", pl[1], pl[0]])
            else:
                r.append(["input ", "",    pl[0]])

    if hatchling.has_property('output_ports'):
        # this hatchling node has some explicitly declared output ports.
        outputPorts = hatchling.property('output_ports')
        outputPorts = outputPorts.replace('\n','')
        while outputPorts.rfind('  ') > 0:
            outputPorts=outputPorts.replace('  ',' ')
        for p in outputPorts.split(','):
            # check for a port size.
            pl = p.split()
            if (len(pl) == 2):
                r.append(["output", pl[1], pl[0]])
            else:
                r.append(["output", "",    pl[0]])

    return r


#======================================================================
#
# These are the main verilog generation functions.
# 
#======================================================================

def get_verilog_declaration_list(hatchling):
    
    """Return a list of declarations required to build this hatchling node."""

    r = []

    if hatchling.property('type') == 'register':

        w = 0
        try:
            w = int(hatchling.property('width'))
        except ValueError:
            raise "*** ERROR: cannot figure out the width of register %s.\n"%(hatchling.name)

        width = ""
        if w > 1:
            width = "[%d:0]"%(w -1)

        if hatchling.has_property('external') :
            # hatchling node is external, so all register functions
            # are implemented outside via a standard 'bus' interface.
            r.append(["wire", width, "%s_in"%(hatchling.name)])
            if  re.match("^.*w.*$", hatchling.property('sw')):
                r.append(["wire", width, "%s_out"%(hatchling.name)])
                r.append(["wire", "",    "%s_we"%(hatchling.name)])
        else:

            # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

            if hatchling.has_property('hw'):

                # add wires for all the hardware ports that exist.
                r.append(["wire", width, "%s_next"%(hatchling.name)])

                if  re.match("^.*w.*$", hatchling.property('hw')):
                    r.append(["wire", width, "%s_in"%(hatchling.name)])
                    r.append(["wire", "", "%s_we"%(hatchling.name)])
                    r.append(["reg ", width, hatchling.name])
                    r.append(["wire", width, "%s_next"%(hatchling.name)])

                if  re.match("^.*d.*$", hatchling.property('hw')):
                    r.append(["wire", "", "%s_dec"%(hatchling.name)])
                    r.append(["reg ", width, hatchling.name])
                    r.append(["wire", width, "%s_next"%(hatchling.name)])

                if re.match("^.*i.*$", hatchling.property('hw')):
                    r.append(["wire", "", "%s_inc"%(hatchling.name)])
                    r.append(["reg ", width, hatchling.name])
                    r.append(["wire", width, "%s_next"%(hatchling.name)])

                if re.match("^.*c.*$", hatchling.property('hw')):
                    r.append(["wire", "", "%s_clear"%(hatchling.name)])
                    r.append(["reg ", width, hatchling.name])
                    r.append(["wire", width, "%s_next"%(hatchling.name)])

                if re.match("^.*s.*$", hatchling.property('hw')):
                    r.append(["wire", "", "%s_set"%(hatchling.name)])
                    r.append(["reg ", width, hatchling.name])
                    r.append(["wire", width, "%s_next"%(hatchling.name)])

                if re.match("^.*p.*$", hatchling.property('hw')):
                    r.append(["wire", "", "%s_preset"%(hatchling.name)])
                    r.append(["reg ", width, hatchling.name])
                    r.append(["wire", width, "%s_next"%(hatchling.name)])

            # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

            if hatchling.has_property('sw'):
                
                # add wires for all the sw functions.
                if  re.match("^.*w.*$", hatchling.property('sw')):
                    r.append(["wire", width, "%s_next"%(hatchling.name)])
                    r.append(["wire", "", "%s_int_we"%(hatchling.name)])
                    r.append(["reg ", width, hatchling.name])
                    r.append(["wire", width, "%s_next"%(hatchling.name)])
                else:
                    r.append(["wire", width, hatchling.name]) # default read term for the register.

                if  re.match("^.*t.*$", hatchling.property('sw')):
                    r.append(["wire", "", "%s_int_we"%(hatchling.name)])
                    if hatchling.has_property('key_value'):
                        # we have a one bit trigger.
                        r.append(["wire", "", "%s_trigger"%(hatchling.name)])
                    else:
                        # the triger is as wide as the register.
                        r.append(["wire", width, "%s_trigger"%(hatchling.name)])
                    # add trigger count support.
                    if hatchling.has_property('trigger_count'):
                        # figure out how wide this has to be.
                        w = int(math.ceil(math.log(hatchling.property('trigger_count')) / math.log(2)))
                        hatchling.set_property('trigger_count_width', w)
                        r.append(["wire", "", "%s_trigger_int"%(hatchling.name)])
                        r.append(["reg ", "[%d:0]"%(w -1), "%s_trigger_count"%(hatchling.name)])

            # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

            if hatchling.has_property('auto'):

                # add wires for all the auto functions.
                r.append(["wire", width, "%s_next"%(hatchling.name)])
                if  re.match("^.*d.*$", hatchling.property('auto')) and hatchling.has_property('auto_dec_on'):
                    r.append(["reg ", width, hatchling.name])
                    for s in signal_list(hatchling.property('auto_dec_on')):
                        r.append(["wire", None, s])

                if re.match("^.*i.*$", hatchling.property('auto')) and hatchling.has_property('auto_inc_on'):
                    r.append(["reg ", width, hatchling.name])
                    for s in signal_list(hatchling.property('auto_inc_on')):
                        r.append(["wire", None, s])

                if re.match("^.*c.*$", hatchling.property('auto')):
                    r.append(["reg ", width, hatchling.name])
                    for s in signal_list(hatchling.property('auto_clear_on')):
                        r.append(["wire", None, s])

                if re.match("^.*s.*$", hatchling.property('auto')):
                    r.append(["reg ", width, hatchling.name])
                    for s in signal_list(hatchling.property('auto_set_on')):
                        r.append(["wire", None, s])

                if re.match("^.*p.*$", hatchling.property('auto')):
                    r.append(["reg ", width, hatchling.name])
                    for s in signal_list(hatchling.property('auto_preset_on')):
                        r.append(["wire", None, s])

                if re.match("^.*t.*$", hatchling.property('auto')):
                    r.append(["reg ", width, hatchling.name])
                    for s in signal_list(hatchling.property('auto_trigger_on')):
                        r.append(["wire", None, s])
                    r.append(["wire", width, "%s_next"%(hatchling.name)])
                    # auto triggers are all 1 bit wide.
                    r.append(["wire", "", "%s_trigger"%(hatchling.name)])
                    # add trigger count support.
                    if hatchling.has_property('trigger_count'):
                        # figure out how wide this has to be.
                        w = int(math.ceil(math.log(hatchling.property('trigger_count')) / math.log(2)))
                        hatchling.set_property('trigger_count_width', w)
                        r.append(["wire", "", "%s_trigger_int"%(hatchling.name)])
                        r.append(["reg ", "[%d:0]"%(w -1), "%s_trigger_count"%(hatchling.name)])

            # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

            if hatchling.has_property('auto_error_detect'):
                pass
                
            if hatchling.has_property('auto_error_correct'):
                pass

            # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    
    if hatchling.has_property('wire') :

        # this hatchling node has some explicitly declared wires so
        # add them.
        wires = hatchling.property('wire')
        wires = wires.replace('\n','')
        while wires.rfind('  ') > 0:
            wires = wires.replace('  ',' ')
        for w in wires.split(','):
            # check for a port size.
            wl = w.split()
            if (len(wl) == 2):
                r.append(["wire", wl[1], wl[0]])
            else:
                r.append(["wire", "",    wl[0]])

    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    if hatchling.has_property('reg') :

        # this hatchling node has some explicitly declared regs so
        # add them.
        regs = hatchling.property('reg')
        regs = regs.replace('\n','')
        while regs.rfind('  ') > 0:
            regs = regs.replace('  ',' ')
        for r in regs.split(','):
            # check for a port size.
            rl = r.split()
            if (len(rl) == 2):
                r.append(["reg ", rl[1], rl[0]])
            else:
                r.append(["reg ", "",    rl[0]])

    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    if hatchling.has_property('input_ports'):
        # this hatchling node has some explicitly declared input ports, so
        # add them as wires.
        inputPorts = hatchling.property('input_ports')
        inputPorts = inputPorts.replace('\n','')
        while inputPorts.rfind('  ') > 0:
            inputPorts=inputPorts.replace('  ',' ')
        for p in inputPorts.split(','):
            # check for a port size.
            pl = p.split()
            if (len(pl) == 2):
                r.append(["wire", pl[1], pl[0]])
            else:
                r.append(["wire", "",    pl[0]])

    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    if hatchling.has_property('output_ports'):
        # this hatchling node has some explicitly declared output ports, so
        # add them as wires.
        outputPorts = hatchling.property('output_ports')
        outputPorts = outputPorts.replace('\n','')
        while outputPorts.rfind('  ') > 0:
            outputPorts=outputPorts.replace('  ',' ')
        for p in outputPorts.split(','):
            # check for a port size.
            pl = p.split()
            if (len(pl) == 2):
                r.append(["wire", pl[1], pl[0]])
            else:
                r.append(["wire", "",    pl[0]])

    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    if hatchling.property('type') == 'pack':
        # generate wires for packs as a convenience or for fun and
        # profit, impress your friends and family.
        if hatchling.name == "":                            # TODO - move this into pack class
            # create a unique name for the pack.
            global uniqueNumber
            hatchling.name = "hatch_pack_%04d"%(uniqueNumber)
            uniqueNumber += 1

        # figure out the width.
        w = 0
        try:
            w = int(hatchling.property('width'))
        except ValueError:
            raise "*** ERROR: cannot figure out the width of pack %s.\n"%(hatchling.name)

        width = ""
        if w > 1:
            width = "[%d:0]"%(w -1)

        r.append(["wire", width, hatchling.name])

    return reduce_declaration_list(r)

#======================================================================

def write_verilog_setup (hatchling, outputFile):

    """write any verilog that the user may have embedded in the
    register definition in the form of rtl statements or any other
    special code required to make the regisers opperate.""" 

    if hatchling.has_property('rtl'):
        outputFile.write("\n  // - - - - - - - - rtl from %s - - - - - - - -\n\n"%(hatchling.name))
        outputFile.write(hatchling.property('rtl'))
        outputFile.write("\n")

#======================================================================

def write_register_verilog (hatchling, outputFile, targetBus):

    """write the verilog required to implement the register to the
    output file given."""

    if hatchling.property('type') == 'register':

        outputFile.write("\n  // - - - - - - - - rtl for register %s - - - - - - - -\n\n"%(hatchling.name))

        triggersUsed = False
        
        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        #
        # the sw write and trigger function.
        #

        if hatchling.has_property('sw') :
            if  re.match("^.*[wt]+.*$", hatchling.property('sw')):
                # implement the software write enable signal used for both
                # writing and triggering the register.
                outputFile.write("  assign %s_int_we = %s & "%(hatchling.name, targetBus.writeEn))
                # add software load enable.
                if hatchling.has_property('sw_load_enable') :
                    outputFile.write("%s & "%(hatchling.property('sw_load_enable')))
                outputFile.write("(%s%s == "%(targetBus.addr, targetBus.addrWidthString))
                outputFile.write("%s);\n"%addr_string(hatchling.property('hatch_address') >> 2,
                                                      targetBus.addrWidth))

            if  re.match("^.*t.*$", hatchling.property('sw')):
                # build a trigger term.
                triggersUsed = True
                if hatchling.has_property('trigger_count'):
                    outputFile.write("  assign %s_trigger_int = "%(hatchling.name))
                else:
                    outputFile.write("  assign %s_trigger = "%(hatchling.name))

                if hatchling.has_property('key_value'):
                    # this is a one bit trigger output.
                    outputFile.write("%s_int_we & ("%(hatchling.name))
                    outputFile.write("%s == "%(value_string(hatchling.property('key_value'),
                                                            hatchling.property('width'))))
                    if hatchling.property('width') > 1:
                        # pick out the correct bits on the taget bus data
                        outputFile.write("%s[%d:%d]);\n"%(targetBus.writeData,
                                                         hatchling.property('width') + hatchling.property('hatch_bit_base') - 1,
                                                         hatchling.property('hatch_bit_base')))
                    else:
                        # pick out the correct target bus data bit.
                        outputFile.write("%s[%d]);\n"%(targetBus.writeData, hatchling.property('hatch_bit_base')))
                else:
                    # this is a regular write 1 to trigger term.
                    if hatchling.property('width') > 1:
                        # make the gating signal wide enaugh to AND with the target bus data.
                        outputFile.write("{%d {%s_int_we}} & "%(hatchling.property('width'), hatchling.name))
                        # pick out the correct bits on the taget bus data
                        outputFile.write("%s[%d:%d];\n"%(targetBus.writeData,
                                                         hatchling.property('width') + hatchling.property('hatch_bit_base') - 1,
                                                         hatchling.property('hatch_bit_base')))
                    else:
                        # this is one bit wide.
                        outputFile.write("%s_int_we & "%(hatchling.name))
                        # pick out the correct target bus data bit.
                        outputFile.write("%s[%d];\n"%(targetBus.writeData, hatchling.property('hatch_bit_base')))


        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        #
        # the auto trigger function.
        #
        # this sets the trigger signal when the contents of the
        # register change to the value described in the
        # auto_trigger_on parameter.
        
        
        if hatchling.has_property('auto') and re.match("^.*t.*$", hatchling.property('auto')):
            triggersUsed = True
            if hatchling.has_property('trigger_count'):
                outputFile.write("  assign %s_trigger_int = "%(hatchling.name))
            else:
                outputFile.write("  assign %s_trigger = "%(hatchling.name))
            outputFile.write("(%s != %s) & "%(hatchling.name,
                                              value_string(hatchling.property('auto_trigger_value'),
                                                           hatchling.property('width'))))
            outputFile.write("(%s_next == %s);\n"%(hatchling.name,
                                                   value_string(hatchling.property('auto_trigger_value'),
                                                                hatchling.property('width'))))
        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        #
        # the trigger count function.
        #
        # this extends the trigger to a multi cycle pulse.
        
        if triggersUsed and hatchling.has_property('trigger_count'):
            # build the trigger counter and final trigger term.
            outputFile.write("  always @ (posedge %s) if (%s) begin\n"%(targetBus.clk,
                                                                        targetBus.clkEn))
            outputFile.write("    if (%s)\n"%(targetBus.reset))
            outputFile.write("      %s_trigger_count <= %d'd0;\n"%(hatchling.name,
                                                                   hatchling.property('trigger_count_width')))
            outputFile.write("    else\n")
            outputFile.write("      %s_trigger_count <= %s_trigger_int ? "%(hatchling.name, hatchling.name))
            outputFile.write("%s :\n"%(value_string(hatchling.property('trigger_count'),
                                                    hatchling.property('trigger_count_width'))))
            outputFile.write("                          %s_trigger_count - "%(hatchling.name))
            outputFile.write("{%d'd0, |%s_trigger_count};\n"%(hatchling.property('trigger_count_width') - 1,
                                                              hatchling.name))
            outputFile.write("  end\n")

            outputFile.write("  assign %s_trigger = %s_trigger_int | (|%s_trigger_count);\n"%(hatchling.name,
                                                                                              hatchling.name,
                                                                                              hatchling.name))

        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        #
        # do we have a register to build?

        buildTest = hatchling.has_property('external')
        buildTest = buildTest or (hatchling.has_property('sw') and re.match("^.*w.*$", hatchling.property('sw')))
        buildTest = buildTest or hatchling.has_property('hw')
        buildTest = buildTest or hatchling.has_property('auto')
    
        if buildTest:
            
            # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            #
            # build the registr_name_next value usign an assign and
            # the ternary opperator to propogate X's and avoid having
            # to compute a sensitivity list which can be tricky if
            # some of the terms used contain expressions.
            #

            s = "  assign %s_next = "%(hatchling.name)
            indent = ""
            outputFile.write(s)

            # precidence matters here.  after some consideration the
            # precidence of opperations is:
            # 1. CPU
            # 2. clear
            # 3. set
            # 4. preset
            # 5. inc / dec

            
            # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            #
            # the cpu write term.
            #

            if hatchling.has_property('sw') and re.match("^.*w.*$", hatchling.property('sw')):
                outputFile.write(indent)
                indent = " " * len(s)
                if hatchling.property('width') > 1:
                    # pick out the correct bits on the taget bus data
                    outputFile.write("%s_int_we ? %s[%d:%d]:\n"%(hatchling.name,
                                                                 targetBus.writeData,
                                                                 hatchling.property('width') + hatchling.property('hatch_bit_base') - 1,
                                                                 hatchling.property('hatch_bit_base')))
                else:
                    # this is one bit wide.
                    outputFile.write("%s_int_we ? %s[%d]:\n"%(hatchling.name,
                                                                 targetBus.writeData,
                                                                 hatchling.property('hatch_bit_base')))

            # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            #
            # the hardware write term.
            #
            
            if hatchling.has_property('hw') and re.match("^.*w.*$", hatchling.property('hw')):
                outputFile.write("%s_we ? %s_in:\n"%(hatchling.name, hatchling.name))
            
            # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            #
            # the clear term.
            #

            if hatchling.has_property('auto') and re.match("^.*c.*$", hatchling.property('auto')):
                outputFile.write(indent)
                indent = " " * len(s)
                outputFile.write("    %s ? %d'h0 : \n"%(hatchling.property('auto_clear_on'), hatchling.property('width')))

            if hatchling.has_property('hw') and re.match("^.*c.*$", hatchling.property('hw')):
                outputFile.write(indent)
                indent = " " * len(s)
                outputFile.write("%s_clear ? %d'h0 : \n"%(hatchling.name, hatchling.property('width')))

            # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            #
            # the set term.
            #

            if hatchling.has_property('auto') and re.match("^.*s.*$", hatchling.property('auto')):
                outputFile.write(indent)
                indent = " " * len(s)
                outputFile.write("%s ? %d'h%x : \n"%(hatchling.property('auto_set_on'),
                                                         hatchling.property('width'),
                                                         2 ** hatchling.property('width') - 1))

            if hatchling.has_property('hw') and re.match("^.*s.*$", hatchling.property('hw')):
                outputFile.write(indent)
                indent = " " * len(s)
                outputFile.write("%s_set ? %d'h%x : \n"%(hatchling.name,
                                                             hatchling.property('width'),
                                                             2 ** hatchling.property('width') - 1))
            
            # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            #
            # the preset term.
            #

            if hatchling.has_property('auto') and re.match("^.*p.*$", hatchling.property('auto')):
                outputFile.write(indent)
                indent = " " * len(s)
                outputFile.write("%s ? %s : \n"%(hatchling.property('auto_preset_on'),
                                                 value_string(hatchling.property('default'),
                                                              hatchling.property('width'))))

            if hatchling.has_property('hw') and re.match("^.*p.*$", hatchling.property('hw')):
                outputFile.write(indent)
                indent = " " * len(s)
                outputFile.write("%s_preset ? %s : \n"%(hatchling.name,
                                                            value_string(hatchling.property('default'),
                                                                         hatchling.property('width'))))
            
            # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            #
            # the inc / dec term.
            #
            # an inc + dec at the same time is a NOP

            hi = hatchling.has_property('hw') and re.match("^.*i.*$", hatchling.property('hw'))
            hd = hatchling.has_property('hw') and re.match("^.*d.*$", hatchling.property('hw'))
            ai = hatchling.has_property('auto') and re.match("^.*i.*$", hatchling.property('auto'))
            ad = hatchling.has_property('auto') and re.match("^.*d.*$", hatchling.property('auto'))

            iTerm = ""
            if hi and ai :
                iTerm = "(%s_inc | %s)"%(hatchling.name, hatchling.property('auto_inc_on'))
            elif hi:
                iTerm = "%s_inc"%(hatchling.name)
            elif ai:
                iTerm = hatchling.property('auto_inc_on')

            dTerm = ""
            if hd and ad :
                dTerm = "(%s_dec | %s)"%(hatchling.name, hatchling.property('auto_dec_on'))
            elif hd:
                dTerm = "%s_dec"%(hatchling.name)
            elif ad:
                dTerm = hatchling.property('auto_dec_on')

            if (hi or ai) and (hd or ad):
                # we have both an inc and a dec term so we need to catch it.
                outputFile.write(indent)
                indent = " " * len(s)
                outputFile.write("(%s & %s) ? %s : \n"%(iTerm, dTerm, hatchling.name))

            if hi or ai:
                # we have an inc term.
                outputFile.write(indent)
                indent = " " * len(s)
                outputFile.write("%s ? %s + %d'h1 : \n"%(iTerm, hatchling.name, hatchling.property('width')))

            if hd or ad:
                # we have a dec term.
                outputFile.write(indent)
                indent = " " * len(s)
                outputFile.write("%s ? %s - %d'h1 : \n"%(dTerm, hatchling.name, hatchling.property('width')))


            #
            # the default value.
            #

            outputFile.write(indent)
            outputFile.write("%s;\n"%(hatchling.name))

            # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            #
            # the synchronous always block.
            #

            if  (hatchling.has_property('hw') and re.match("^.*[wscidp]+.*$", hatchling.property('hw'))) or hatchling.has_property('auto'):
                # there is some automatic or external HW manipulation
                # of the register so we cannot use the gated clock for
                # this register.
                outputFile.write("  always @ (posedge %s) if (%s) begin\n"%(targetBus.clk,
                                                                            targetBus.clkEn))
            else:
                # this is a vanila config register. don't clock it
                # when we are not writing to it.
                outputFile.write("  always @ (posedge %s) if (%s) begin\n"%(targetBus.gatedClk,
                                                                            targetBus.gatedClkEn))

            # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            #
            # the reset term, load the default value for the register.
            #
            
            outputFile.write("    if (%s)\n"%(targetBus.reset))
            outputFile.write("      %s <= %s;\n"%(hatchling.name,
                                                  value_string(hatchling.property('default'),
                                                               hatchling.property('width'))))
            
            # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            #
            # the register guts.
            #
            outputFile.write("    else\n")
            outputFile.write("      %s <= %s_next;\n"%(hatchling.name, hatchling.name))

            # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

            # end of synchronous block
            outputFile.write("  end\n")

        else:
            
            # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            #
            # We do not have any register to build.
            #

            outputFile.write("  assign %s = %s;\n"%(hatchling.name,
                                                    value_string(hatchling.property('default'),
                                                                 hatchling.property('width'))))

        outputFile.write("\n  // - - - - - - - - end of rtl for register %s - - - - - - - -\n\n"%(hatchling.name))
        
    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    if hatchling.property('type') == 'pack':
        # generate a convenience wire that collects all the registers
        # of a pack into a single wire.

        outputFile.write("\n  // - - - - - - - - - rtl for pack %s - - - - - - - - - -\n\n"%(hatchling.name))

        outputFile.write("  assign %s = {"%(hatchling.name))
        for c in hatchling.contentList[:-1]:
            outputFile.write("%s, "%(c.name))
        outputFile.write("%s};\n"%(hatchling.contentList[-1].name))

        outputFile.write("\n  // - - - - - - - - end of rtl for pack %s - - - - - - - -\n\n"%(hatchling.name))


#======================================================================

def read_register_sensitivity_list (hatchling):

    """return a list of signals to put in the sensitivity list for the
    CPU read function"""

    r = []
    
    buildTerm = hatchling.property('type') == 'register' and hatchling.parent.property('type') != 'pack'
    buildTerm = buildTerm or hatchling.property('type') == 'pack'
    
    if buildTerm:
        # only return registrs that are not in packs and packs.
        r.append(hatchling.name)

    return r

            
#======================================================================

def read_register_verilog (hatchling, outputFile, targetBus):

    """write the verilog required to implement CPU read functionality
    to the output file given."""

    # only build read terms for packs and registrs that are not in packs.
    buildTerm = hatchling.property('type') == 'pack'
    buildTerm = buildTerm or (hatchling.property('type') == 'register' and hatchling.parent.property('type') != 'pack')

    if buildTerm:

        #
        # address decode
        #

        outputFile.write("      %s : %s <= "%(addr_string(hatchling.property('hatch_address') >> 2,
                                                          targetBus.addrWidth),
                                              targetBus.readData))

        #
        # padd the register or pack to fill the full data bus width.
        #

        if hatchling.property('width') > targetBus.dataWidth:
            raise """*** ERROR - cannot support %d bit register %s, only %d bit registers or 
less are supported."""%(hatchling.property('width'), 
                      hatchling.name,
                      targetBus.dataWidth)
        elif hatchling.property('width') == targetBus.dataWidth:
            # simple case.
            outputFile.write("%s;\n"%(hatchling.name))
        else:
            # pad.
            outputFile.write("{%d'h0, %s};\n"%(targetBus.dataWidth - hatchling.property('width'),
                                               hatchling.name))
            
#======================================================================

def generate_verilog (hatchling, moduleName):

    """Write out the complete verilog module description required to
    implement the registers described in the hatch datastructure."""

    # attempt to open a file for writing.
    fileName = moduleName + ".v"
    outputFile = open(fileName, 'w')

    # get the target bus
    if not hatchling.has_property('target_bus'):
        raise "*** ERROR - cant seem to find a target bus definition in %s."%(hatchling.name)
    targetBus = hatchling.property('target_bus')

    # some debug.
    # print hatchling
    # print hatchling.property('target')
    # print "address range %d"%(targetBus.addrRange)

    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    # write out the header.
    outputFile.write(verilogHeader)
    if hatchling.has_property('header'):
        outputFile.write(hatchling.property('header'))

    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    # build the module definition
    outputFile.write("\nmodule %s\n"%(moduleName))

    # get the list of ports we need to make this module.
    portList  = targetBus.get_port_list()
    portList += hatchling.run_on_all_return_list(get_verilog_port_list)

    # check the port list as we generate the module port list.
    portHash = {}
    s = "  ("
    for (m, w, n) in portList[:-1]:
        s += "%s, "%(n)
        if len(s) > 60:
            outputFile.write("%s\n"%(s))
            s = "   "
        if portHash.has_key(n):
            raise "*** ERROR - port %s is multiply defined in hatchfile %s."%(n, moduleName)
        else:
            portHash[n] = 1
    (m, w, n) = portList[-1]
    s += "%s);\n\n\n"%(n)
    if portHash.has_key(n):
        raise "*** ERROR - port %s is multiply defined in hatchfile %s."%(n, moduleName)
    del portHash
    outputFile.write(s)

    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    # now write out all the port mode definitions.
    outputFile.write("  //---------------------------------------------------------------------\n")
    outputFile.write("  // port mode definition\n\n")
    for (m, w, n) in portList:
        s = "  %s %s"%(m, w)
        s += " " * (20 - len(s))
        outputFile.write("%s %s;\n"%(s, n))
    outputFile.write("\n\n")

    # now write out all the wires and reg definitions required by this module.
    declarationList  = targetBus.get_declaration_list()
    declarationList += hatchling.run_on_all_return_list(get_verilog_declaration_list)
    declarationList = reduce_declaration_list(declarationList)
    outputFile.write("  //---------------------------------------------------------------------\n")
    outputFile.write("  // register and wire definitions\n\n")
    for (m, w, n) in declarationList:
        # wires declared with a width of None imply the width is
        # unknown and are a place holder for a wire or register
        # declaration somewhere else in the design.  if any wires of
        # width None are found when writing out the design, it imples
        # that a signal is used in some expression that is not
        # preovided in the register definition file.
        if w == None:
            raise "*** ERROR - %s %s is required to implement some function but is not driven by anything."%(m, n)
        s = "  %s %s"%(m, w)
        s += " " * (20 - len(s))
        outputFile.write("%s %s;\n"%(s, n))
    outputFile.write("\n\n")

    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    # find all the setup code and write it out now.  This is code that
    # the user may have embedded in the register definition in the
    # form of rtl statements or it may be code required by the target
    # bus to do some setup.

    outputFile.write("  //---------------------------------------------------------------------\n")
    outputFile.write("  // setup code.\n\n")

    targetBus.write_verilog_setup(outputFile)
    hatchling.run_on_all(write_verilog_setup, outputFile=outputFile)

    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    # now generate all the code for all the registers and packs defined
    # in the hatchling.    
    hatchling.run_on_all(write_register_verilog, outputFile=outputFile, targetBus=targetBus)
    
    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    # now generate the CPU read structure.

    sensitivityList  = [targetBus.addr]
    sensitivityList += hatchling.run_on_all_return_list(read_register_sensitivity_list)

    s = "  always @("
    indent = " " * len(s)
    for r in sensitivityList[:-1]:
        s += "%s, "%(r)
        if len(s) > 60:
            outputFile.write("%s\n"%(s))
            s = indent
    r = sensitivityList[-1]
    s += "%s) begin\n"%(r)
    outputFile.write(s)

    outputFile.write("    casez(%s%s)\n"%(targetBus.addr, targetBus.addrWidthString))

    hatchling.run_on_all(read_register_verilog, outputFile=outputFile, targetBus=targetBus)

    outputFile.write("      default: %s <= %d'd0;\n"%(targetBus.readData, targetBus.dataWidth))
    outputFile.write("    endcase // case(%s)\n"%(targetBus.addr))
    outputFile.write("  end\n")

    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    #
    # done!
    
    outputFile.write("\n  //---------------------------------------------------------------------\n\n")

    outputFile.write("endmodule\n")

    outputFile.close()

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

#----------------------------------------------------------------------
